Spatial information of a unitcell
The first step toward completely describing a quantum lattice system is understanding the spatial information of a unitcell.
Construction of a lattice
In general, a lattice has translation symmetry. This symmetry introduces an equivalence relation for the points in a lattice when they can be translated into each other by multiples of the translation vectors. This observation sets the mathematical foundation for the unitcell construction. As a result, it is sufficient to represent a lattice by restricting all points within the origin unitcell along with the translation vectors.
Lattice is the simplest structure to encode all the spatial information within the origin unitcell. It must contain the coordinates of all points in the origin unitcell and the translation vectors of the lattice. It is also useful to associate a lattice with a name. Therefore, in this package, Lattice has three attributes:
name::Symbol: the name of the latticecoordinates::Matrix{<:Number}: the coordinates of the points within the origin unitcellvectors::Vector{<:StaticArraysCore.SVector}: the translation vectors of the lattice
Lattice can be constructed by providing the coordinates, with optional keyword arguments to specify its name and translation vectors:
julia> Lattice([0.0])
Lattice(lattice)
with 1 point:
[0.0]
julia> Lattice((0.0, 0.0), (0.5, 0.5); vectors=[[1.0, 0.0], [0.0, 1.0]], name=:Square)
Lattice(Square)
with 2 points:
[0.0, 0.0]
[0.5, 0.5]
with 2 translation vectors:
[1.0, 0.0]
[0.0, 1.0]
julia> Lattice(
(0.0, 0.0, 0.0);
name=:Cube,
vectors=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
)
Lattice(Cube)
with 1 point:
[0.0, 0.0, 0.0]
with 3 translation vectors:
[1.0, 0.0, 0.0]
[0.0, 1.0, 0.0]
[0.0, 0.0, 1.0]The coordinates can be specified using vectors or tuples.
Iteration over a lattice yields the coordinates of the points in it:
julia> lattice = Lattice((0.0, 0.0), (0.5, 0.5); vectors=[[1.0, 0.0], [0.0, 1.0]]);
julia> length(lattice)
2
julia> [lattice[1], lattice[2]]
2-element Vector{StaticArraysCore.SVector{2, Float64}}:
[0.0, 0.0]
[0.5, 0.5]
julia> collect(lattice)
2-element Vector{StaticArraysCore.SVector{2, Float64}}:
[0.0, 0.0]
[0.5, 0.5]The reciprocal translation vectors of the dual lattice can be obtained by reciprocals:
julia> lattice = Lattice((0.0, 0.0); vectors=[[1.0, 0.0], [0.0, 1.0]]);
julia> reciprocals(lattice)
2-element StaticArraysCore.SVector{2, StaticArraysCore.SVector{2, Float64}} with indices SOneTo(2):
[6.283185307179586, -0.0]
[-0.0, 6.283185307179586]Request for the bonds of a lattice
Before explaining how to obtain the bonds of a lattice, let's discuss the unitcell construction further to clarify the logic behind the definitions of the Point type and the Bond type in this package.
Point
With translation symmetry, all points of a lattice are equivalent to those within the origin unitcell. However, things become complicated when bonds are requested. Bonds between different unitcells cannot be compressed into a single unitcell. Therefore, even in the unitcell construction framework, it is necessary to specify a point outside the origin unitcell, which requires extra information beyond a single coordinate if we want to remember which point it is equivalent to within the origin unitcell at the same time.
In the literature, it is customary to express the coordinate $\mathbf{R}$ of a point in a lattice as $\mathbf{R} = \mathbf{R}_i + \mathbf{r}$, where $\mathbf{R}_i$ is the integral coordinate of the unitcell the point belongs to and $\mathbf{r}$ is the relative displacement of the point in the unitcell. Any two of these three coordinates are sufficient to get the full information. In this package, we choose $\mathbf{R}$ and $\mathbf{R}_i$ as the complete set for an individual lattice point. Additionally, we also associate a site index with a point for fast lookup of its equivalence within the origin unitcell, although this is redundant in theory. Thus, the Point defined in this package has three attributes as follows:
site::Int: the site index of a point that specifies the equivalent point within the origin unitcellrcoordinate::StaticArraysCore.SVector: the real coordinate of the point ($\mathbf{R}$)icoordinate::StaticArraysCore.SVector: the integral coordinate of the unitcell the point belongs to ($\mathbf{R}_i$)
When constructing a Point, rcoordinate and icoordinate can accept tuples or standard vectors as inputs, such as:
julia> Point(1, [0.0], [0.0])
Point(1, [0.0], [0.0])
julia> Point(1, (1.5, 0.0), (1.0, 0.0))
Point(1, [1.5, 0.0], [1.0, 0.0])icoordinate can be omitted, then it will be initialized by a zero StaticArraysCore.SVector:
julia> Point(1, [0.0, 0.5])
Point(1, [0.0, 0.5], [0.0, 0.0])Bond
A bond in the narrow sense consists of two points. However, in quantum lattice systems, it is common to refer to generic bonds with only one or more than two points. Additionally, it is convenient to associate a bond with kind information, such as the order of the nearest neighbors of the bond. Thus, the Bond is defined as follows:
kind: the kind information of a generic bondpoints::Vector{<:Point}: the points a generic bond contains
julia> Bond(Point(1, [0.0, 0.0], [0.0, 0.0])) # 1-point bond
Bond(0, Point(1, [0.0, 0.0], [0.0, 0.0]))
julia> Bond(2, Point(1, [0.0, 0.0], [0.0, 0.0]), Point(1, [1.0, 1.0], [1.0, 1.0])) # 2-point bond
Bond(2, Point(1, [0.0, 0.0], [0.0, 0.0]), Point(1, [1.0, 1.0], [1.0, 1.0]))
julia> Bond(:plaquette, Point(1, [0.0, 0.0]), Point(2, [1.0, 0.0]), Point(3, [1.0, 1.0]), Point(4, [0.0, 1.0])) # generic bond with 4 points
Bond(:plaquette, Point(1, [0.0, 0.0], [0.0, 0.0]), Point(2, [1.0, 0.0], [0.0, 0.0]), Point(3, [1.0, 1.0], [0.0, 0.0]), Point(4, [0.0, 1.0], [0.0, 0.0]))Note that the kind attribute of a bond with only one point is set to 0.
Iteration over a bond will yield the points it contains:
julia> bond = Bond(2, Point(1, [0.0, 0.0], [0.0, 0.0]), Point(2, [1.0, 0.0], [0.0, 0.0]));
julia> length(bond)
2
julia> [bond[1], bond[2]]
2-element Vector{Point{2, Float64}}:
Point(1, [0.0, 0.0], [0.0, 0.0])
Point(2, [1.0, 0.0], [0.0, 0.0])
julia> collect(bond)
2-element Vector{Point{2, Float64}}:
Point(1, [0.0, 0.0], [0.0, 0.0])
Point(2, [1.0, 0.0], [0.0, 0.0])The coordinate of a bond as a whole is also defined for those that only contain one or two points. The coordinate of a 1-point bond is defined to be the corresponding coordinate of this point, and the coordinate of a 2-point bond is defined to be the corresponding coordinate of the second point minus that of the first:
julia> bond1p = Bond(Point(1, [2.0], [1.0]));
julia> rcoordinate(bond1p)
1-element StaticArraysCore.SVector{1, Float64} with indices SOneTo(1):
2.0
julia> icoordinate(bond1p)
1-element StaticArraysCore.SVector{1, Float64} with indices SOneTo(1):
1.0
julia> bond2p = Bond(1, Point(1, [1.0, 1.0], [1.0, 1.0]), Point(2, [0.5, 0.5], [0.0, 0.0]));
julia> rcoordinate(bond2p)
2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2):
-0.5
-0.5
julia> icoordinate(bond2p)
2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2):
-1.0
-1.0Generation of 1-point and 2-point bonds of a lattice
In this package, we provide the function bonds to get the 1-point and 2-point bonds of a lattice:
bonds(lattice::Lattice, nneighbor::Int) -> Vector{<:Bond}
bonds(lattice::Lattice, neighbors::Neighbors) -> Vector{<:Bond}This function is based on the KDTree type provided by the NearestNeighbors.jl package. In the first method, all bonds up to the nneighborth nearest neighbors are returned, including the 1-point bonds:
julia> lattice = Lattice([0.0, 0.0]; vectors=[[1.0, 0.0], [0.0, 1.0]]);
julia> bonds(lattice, 2)
5-element Vector{Bond{Int64, Point{2, Float64}}}:
Bond(0, Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(2, Point(1, [-1.0, -1.0], [-1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(1, Point(1, [0.0, -1.0], [0.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(2, Point(1, [1.0, -1.0], [1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(1, Point(1, [-1.0, 0.0], [-1.0, 0.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))However, this method is not very efficient, as KDTree only searches for bonds with lengths less than a given value, and it does not know the bond lengths for each order of nearest neighbors. This information must be computed first. Therefore, in the second method, bonds can accept a new type, Neighbors, as its second positional parameter to improve efficiency, as it can tell the program the bond length information a priori:
julia> lattice = Lattice([0.0, 0.0]; vectors=[[1.0, 0.0], [0.0, 1.0]]);
julia> bonds(lattice, Neighbors(0=>0.0, 1=>1.0, 2=>√2))
5-element Vector{Bond{Int64, Point{2, Float64}}}:
Bond(0, Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(2, Point(1, [-1.0, -1.0], [-1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(1, Point(1, [0.0, -1.0], [0.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(2, Point(1, [1.0, -1.0], [1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(1, Point(1, [-1.0, 0.0], [-1.0, 0.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))Meanwhile, an instance of Neighbors can also serve as a filter for the generated bonds, selecting those bonds with the given bond lengths:
julia> lattice = Lattice([0.0, 0.0]; vectors=[[1.0, 0.0], [0.0, 1.0]]);
julia> bonds(lattice, Neighbors(2=>√2))
2-element Vector{Bond{Int64, Point{2, Float64}}}:
Bond(2, Point(1, [-1.0, -1.0], [-1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))
Bond(2, Point(1, [1.0, -1.0], [1.0, -1.0]), Point(1, [0.0, 0.0], [0.0, 0.0]))To obtain generic bonds containing more points, users are encouraged to implement their own bonds methods. Pull requests are welcome.